package studio.raptor.dispatcher.server.sort;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.SetMultimap;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.stereotype.Component;
import studio.raptor.databus.command.EventMetaData;

/**
 * 事件之间的关联关系
 * 按照接口维度存储，每个接口对应对个关联关系
 *
 * @author jack
 * @since 1.0
 */
@Component
public class EventRelationship {

  //key:code 接口编码，value:接口事件之间的关联关系
  private Map<String, Relationship> serviceRelationship = new ConcurrentHashMap<>(50);


  public class Relationship {

    private final Lock lock = new ReentrantLock();

    //key:partyId,value:objectKey
    private Map<Long, String> partyRule = new HashMap<>();
    //key:prodId,value:objectKey
    private Map<Long, String> prodRule = new HashMap<>();
    //key:partyId,value:objectKey
    private Map<Long, String> modifyPartyRule = new HashMap<>();
    //key:accountId,value:objectKey
    private Map<Long, String> accountRule = new HashMap<>();
    //key:accountId,value:objectKey
    private Map<Long, String> modifyAccountRule = new HashMap<>();
    //key:offerId,value:objectKey
    private Map<Long, String> offerRule = new HashMap<>();


    private SetMultimap<String, Long> partyRuleReverse = HashMultimap.create();
    private SetMultimap<String, Long> modifyPartyRuleReverse = HashMultimap.create();
    private SetMultimap<String, Long> prodRuleReverse = HashMultimap.create();
    //key:objectKey,value:[acctId]
    private SetMultimap<String, Long> accountRuleReverse = HashMultimap.create();
    //key:objectKey,value:[acctId]
    private SetMultimap<String, Long> modifyAccountRuleReverse = HashMultimap.create();
    //key:objectKey,value:[offerId]
    private SetMultimap<String, Long> offerRuleReverse = HashMultimap.create();

    public Map<Long, String> getPartyRule() {
      return partyRule;
    }

    public Map<Long, String> getProdRule() {
      return prodRule;
    }

    public Map<Long, String> getModifyPartyRule() {
      return modifyPartyRule;
    }

    public Map<Long, String> getAccountRule() {
      return accountRule;
    }

    public Map<Long, String> getModifyAccountRule() {
      return modifyAccountRule;
    }

    public Map<Long, String> getOfferRule() {
      return offerRule;
    }

    public SetMultimap<String, Long> getPartyRuleReverse() {
      return partyRuleReverse;
    }

    public SetMultimap<String, Long> getModifyPartyRuleReverse() {
      return modifyPartyRuleReverse;
    }

    public SetMultimap<String, Long> getProdRuleReverse() {
      return prodRuleReverse;
    }

    public SetMultimap<String, Long> getAccountRuleReverse() {
      return accountRuleReverse;
    }

    public SetMultimap<String, Long> getModifyAccountRuleReverse() {
      return modifyAccountRuleReverse;
    }

    public SetMultimap<String, Long> getOfferRuleReverse() {
      return offerRuleReverse;
    }

    public Lock getLock() {
      return lock;
    }
  }


  /**
   * 获取当前事件依赖的事件ID
   *
   * @param code 接口编码
   * @param event 当前事件
   * @return 当前事件依赖的事件id集合
   */
  public Set<String> getDependEventId(String code, EventMetaData event) {
    Relationship relationship = getRelationship(code);
    Lock lock = relationship.getLock();
    lock.lock();
    try {
      DefaultSortRule sortingRule = new DefaultSortRule(event, relationship);
      Set<String> dependOrderIds = sortingRule.sortByProd().sortByParty().sortByModifyParty()
          .sortByAcct()
          .sortByMofifyAcct().sortByOffer()
          .getDependIds();
      if (dependOrderIds.contains(event.getBusinessNo())) {
        dependOrderIds.remove(event);
      }
      return dependOrderIds;
    } finally {
      lock.unlock();
    }
  }

  /**
   * 根据接口编码获取接口对应的事件依赖关系
   *
   * @param code 接口编码
   * @return 如果不存在返回一个空的关系
   */
  public Relationship getRelationship(String code) {
    Objects.requireNonNull(code);
    if (serviceRelationship.containsKey(code)) {
      return serviceRelationship.get(code);
    }

    Relationship relationship = new Relationship();
    serviceRelationship.put(code, relationship);
    return relationship;

  }

  /**
   * 更新接口对应的事件依赖关系
   *
   * @param orderId 当前任务的订单id
   * @param code 接口编码
   */
  public void updateRelationship(String orderId, String code) {
    Relationship relationship = serviceRelationship.get(code);

    Lock lock = relationship.getLock();
    lock.lock();
    try {
      //party
      Set<Long> partyIds = relationship.getPartyRuleReverse().get(orderId);
      for (Long partyId : partyIds) {
        relationship.getPartyRule().remove(partyId);
      }
      relationship.getPartyRuleReverse().removeAll(orderId);

      //modify party
      Set<Long> modifyPartyIds = relationship.getModifyPartyRuleReverse().get(orderId);
      for (Long partyId : modifyPartyIds) {
        relationship.getModifyPartyRule().remove(partyId);
      }
      relationship.getModifyPartyRuleReverse().removeAll(orderId);

      //prod
      Set<Long> prodIds = relationship.getPartyRuleReverse().get(orderId);
      for (Long prodId : prodIds) {
        relationship.getProdRule().remove(prodId);
      }
      relationship.getProdRuleReverse().removeAll(orderId);

      // modify account
      Set<Long> modifyAcctIds = relationship.getModifyAccountRuleReverse().get(orderId);
      for (Iterator<Long> ite = modifyAcctIds.iterator(); ite.hasNext(); ) {
        relationship.getModifyAccountRule().remove(ite.next());
      }
      relationship.getModifyAccountRuleReverse().removeAll(orderId);
      //acct
      Set<Long> acctIds = relationship.getAccountRuleReverse().get(orderId);
      for (Iterator<Long> ite = acctIds.iterator(); ite.hasNext(); ) {
        relationship.getAccountRule().remove(ite.next());
      }
      relationship.getAccountRuleReverse().removeAll(orderId);

      //offer
      Set<Long> offerIds = relationship.getOfferRuleReverse().get(orderId);
      for (Iterator<Long> ite = offerIds.iterator(); ite.hasNext(); ) {
        relationship.getOfferRule().remove(ite.next());
      }
      relationship.getOfferRuleReverse().removeAll(orderId);
    } finally {
      lock.unlock();
    }
  }

}
